﻿<template>
    <div style="width:100%;">
        <div class="van-cell" v-for="file in fileList" :key="file">
            <div v-if="showIcon" @click.prevent="download(file)" style="cursor: pointer;">
                <svg class="icon" style="width:42px;height: 48px;" aria-hidden="true">
                    <use v-bind:xlink:href="fileIcon(file.ext)"></use>
                </svg>
            </div>

            <div class="van-cell__title">
                <span>{{ file.name }}</span>
                <div class="van-cell__label" v-if="file.size > 0">{{ formatSize(file.size) }}</div>
            </div>

            <el-icon :size="24" color="#283486" @click="download(file)" style="cursor: pointer;">
                <Download />
            </el-icon>

            <el-icon :size="24" v-if="!disabled" @click="del(file)" style="cursor: pointer;">
                <Close />
            </el-icon>
        </div>

        <a href="#" ref="download_a" :download="cur_filename" target="_blank" style="display:none;">{{ cur_filename }}</a>

        <div style="display:flow;" v-if="canUpload">
            <el-button :disabled="percent != 0" type="default" v-on:click="doUpload">上传附件</el-button>
            <el-button v-if="isMiniProgram" :disabled="percent != 0" type="default" v-on:click="uploadWechatFile">上传聊天记录文件</el-button>
            <span v-if="percent != 0">{{ percentTxt }}</span>
        </div>
        <input type="file" ref="inputRef" :accept="fileAccept" v-on:change="handleChange" style="display:none" />
        <el-progress v-if="percent != 0" :stroke-width="15" striped striped-flow :percentage="percent" style="width:100%" />
    </div>
</template>

<script>
import './file-type-icon/iconfont'
import './file-type-icon/file-type-icon.css'
import { Plus, Close, Download } from '@element-plus/icons-vue'
import store from './store.js';
import Utils from './Utils.js';

const FileTypeIcons = [
    { type: ".xls;.xlsx,.csv", icon: "#icon-yunpanlogo-" },
    { type: ".doc,.docx", icon: "#icon-yunpanlogo-2" },
    { type: ".rar,.zip,.7z,.gz", icon: "#icon-yunpanlogo-6" },
    { type: ".pdf", icon: "#icon-yunpanlogo-11" },
    { type: ".ppt,.pptx,.dps,.dpt,.pps,.ppsx,.pot,potx", icon: "#icon-yunpanlogo-1" },
    { type: ".mp4,.mov,.avi,.m4v,.mpeg", icon: "#icon-yunpanlogo-7" },
    { type: ".jpg,.png,.jpeg,.gif,.webp", icon: "#icon-yunpanlogo-10" },
    { type: ".mp3", icon: "#icon-yunpanlogo-4" },
    { type: ".txt,.xml,.json,.ini,.log", icon: "#icon-yunpanlogo-8" },
    { type: ".htm,.html,.cshtml,.vue", icon: "#icon-yunpanlogo-8" },
]
export default {
    name: 'FileUploader',
    emits: ['update:modelValue', "uploadOK", "remove"],
    components: { Plus, Close, Download },
    props: {
        modelValue: {
            type: String,
            default: "",
        },
        formData:Object,
        disabled: Boolean,
        extData: Object,
        server: String,
        ossServer: Boolean,
        headers: {
            type: Object,
            default: function () { return {} },
        },
        max: {
            type: Number,
            default: 9
        },
        maxSize: {
            type: Number,
            default: 100
        },
        size: {
            type: Number,
            default: 100
        },
        fileAccept: {
            type: String,
            default: "*.*"
        },
        showIcon:{
            type:Boolean,
            default:true
        }
    },
    data: function () {
        return {
            percent: 0,
            percentTxt: "",
            cur_filename: '',
            isMiniProgram: false
        }
    },
    mounted() {
        this.onProgress(0)
        this.checkWechatEnvironment()
    },
    methods: {
        checkWechatEnvironment() {
            // 检查是否在微信环境中
            const ua = navigator.userAgent.toLowerCase()
            this.isMiniProgram = ua.indexOf('miniprogram') !== -1 
                && typeof wx !== 'undefined'
                && wx.miniProgram  !== 'undefined'
                && window.miniProgramHybrid !== 'undefined';
        },
        getHeader(){
            const token = document.cookie.split(';').map(x=>x.split("=")).filter(x=>x[0].endsWith("token"))[0];
            if(token){
                return {"token":token[1]}
            }
            return {};
        },
        
        uploadWechatFile() {
            if (!this.isMiniProgram) return
            let postUrl = this.getUrl(this.server || store.UploadServer)
            postUrl = location.protocol + "//" + location.hostname + ":" + location.port + postUrl
            const param = {
                url: location.href,
                type:"all",
                post_url:  postUrl,
                pm:{
                    ...this.extData, 
                    toOss: this.ossServer?1:0
                },
                header: this.getHeader()
            }
            miniProgramHybrid.call("uploadFile", param).then(res => {
                if (!res.success) {
                    alert(res.message)
                    return
                }
                this.$emit("uploadOK", { url: res.url, name: res.name });
                var item = { name: res.name, type: res.type, size: res.size, url: res.url, icon: "" }
                this.fileList = this.fileList.concat([item])
            })
        },
        del(item) {
            this.$emit("remove", item.url);
            this.fileList = this.fileList.filter(p => p.url != item.url)
        },
        getUrl(url) {
            return store.getUrl(url);
        },
        download(item) {
            var a = this.$refs.download_a;
            a.href = store.getUrl(item.url);
            this.cur_filename = item.name;
            this.$nextTick(() => {
                a.click();
            });
        },
        handleChange: function (e) {
            const files = e.target.files;
            if (!files) return;
            this.uploadFiles(files);
        },
        uploadFiles: function (files) {
            for (var i = 0; i < files.length; i++) {
                var file = files[i]
                if (file.size > this.maxSize2 * 1024 * 1024) {
                    $msg.alert(`最大支持${this.maxSize2}M大小的文件`)
                    return
                }

                if (this.ossServer) {
                    let pm = {
                        ...this.extData,
                        filename: file.name
                    }
                    //向服务器查询是否可以接受的文件
                    $server.call("/Common/OssSignature", pm).then(res => {
                        //上传到OSS服务器上
                        var data = [];
                        data['key'] = 'NSO' + res.key;
                        data['OSSAccessKeyId'] = res.OSSAccessKeyId;
                        data['policy'] = res.policy;
                        data['signature'] = res.signature;
                        data['success_action_status'] = '200';
                        data['Content-Disposition'] = 'attachment;filename="' + file.name + '"'

                        let ossHost = "//" + res.ossHost;
                        store.OSSEndPont = ossHost;

                        let okurl = "/NSO" + res.key;

                        this.post(file, ossHost, data, okurl);
                    })
                } else {
                    this.post(file)
                }
            }
        },
        post: function (rawFile, host, extData, okurl) {
            const xhr = new XMLHttpRequest()
            const action = host || this.server || store.UploadServer;

            var that = this;
            if (xhr.upload) {
                xhr.upload.onprogress = function progress(e) {
                    if (e.total > 0) {
                        e.percent = parseFloat((e.loaded / e.total * 100).toFixed(2))
                        that.onProgress(e.percent, e.loaded, e.total)
                    }
                }
            }
            that.onProgress(0)
            const formData = new FormData()

            if (extData) {
                Object.keys(extData).forEach(key => {
                    formData.append(key, extData[key])
                })
            }

            if (this.extData && !this.ossServer) {
                var pformData = Utils.getFormData(this);
                Object.keys(this.extData).forEach(key => {
                    //设置函数，返回动态值，比如当前数据的主键...
                    //这样能把附件和记录关联起来， 
                    //例如：访问附件的时候，检查是否有该条数据的访问权限
                    let val1 = this.extData[key];
                    if (typeof (val1) == "function") {
                        val1 = val1.apply(this, [pformData]);
                    }
                    formData.append(key, val1)
                })
            }

            formData.append("file", rawFile, rawFile.name)
            xhr.onerror = function error() {
                that.onProgress(0)
                that.onError(xhr)
            }

            xhr.onload = function onload() {
                if (xhr.status < 200 || xhr.status >= 300) {
                    that.onError(xhr)
                    return
                }

                var text = xhr.responseText || xhr.response
                if (okurl) {
                    text = { success: true, url: okurl, name: rawFile.name };
                } else {
                    try {
                        text = JSON.parse(text)
                    } catch { }
                }
                that.onProgress(0)
                that.onSuccess(text, rawFile)
            }

            xhr.open('post', action, true)

            if ('withCredentials' in xhr) {
                xhr.withCredentials = false
            }

            const headers = that.headers || {}

            for (const item in headers) {
                if (headers.hasOwnProperty(item) && headers[item] !== null) {
                    xhr.setRequestHeader(item, headers[item])
                }
            }
            if(store.JWTToken){
                xhr.setRequestHeader('Authorization',`Bearer ${store.JWTToken}`)
            }

            xhr.send(formData)
            return xhr

        },
        onSuccess: function (res, rawFile) {
            //在这里处理服务端返回的附件路径
            if (!res.success) {
                alert(res.message)
                return
            }
            this.$emit("uploadOK", { url: res.url, name: rawFile.name });
            var item = { name: rawFile.name, type: rawFile.type, size: rawFile.size, url: res.url, icon: "" }
            this.fileList = this.fileList.concat([item])
        },
        onError: function (err) {
            alert(err)
        },
        onProgress: function (percent, loaded, total) {
            this.percent = parseInt(percent);
            if (this.percent == 0 || !loaded || !total) {
                this.percentTxt = "";
            } else {
                this.percentTxt = this.formatSize(loaded) + "/" + this.formatSize(total);
            }
        },


        doUpload: function () {
            if (this.disabled) return;
            this.$refs.inputRef.value = null;
            this.$refs.inputRef.click();
        },

        /**
         * 格式化文件大小, 输出成带单位的字符串
         * @param {Number} size 文件大小
         * @param {Number} [pointLength=2] 精确到的小数点数。
         * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。
         *    如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.
         */
        formatSize(size, pointLength, units) {
            var unit;
            units = units || ['B', 'K', 'M', 'G', 'TB'];
            while ((unit = units.shift()) && size > 1024) {
                size = size / 1024;
            }
            return (unit === 'B' ? size : size.toFixed(pointLength === undefined ? 2 : pointLength)) + unit;
        },

        fileIcon(ext) {
            for (var i = 0; i < FileTypeIcons.length; i++) {
                var type = FileTypeIcons[i].type
                if (type.indexOf(ext) != -1) return FileTypeIcons[i].icon;
            }

            return "#icon-yunpanlogo-3"
        },

        acceptFile(fn) {
            var ext = fn.substr(fn.lastIndexOf("."));
            var icon = this.fileIcon(ext);
            return icon != "#icon-yunpanlogo-3";
        }


    },
    computed: {
        fileList: {
            get() {
                var list = []
                var str = this.modelValue || "";
                var files = str.split(';').filter(s => s != '')
                files.forEach(file => {
                    var attr = file.split(':')
                    if (attr.length == 4) {
                        var url = attr[0]
                        var name = attr[1]
                        var size = attr[2]
                        var type = attr[3]
                        var ext = url.substr(url.lastIndexOf(".")).toLowerCase()
                        list.push({ name, url, ext, icon: "", size, type })
                    } else if (attr.length > 1) {
                        var url = attr[0]
                        var name = attr[1]
                        var size = 0
                        var ext = name.substr(name.lastIndexOf(".")).toLowerCase()
                        list.push({ name, url, ext, icon: "", size, type: "" })
                    } else {
                        var url = attr[0]
                        var name = url.substr(url.lastIndexOf("/") + 1)
                        var size = 0
                        var ext = name.substr(name.lastIndexOf(".")).toLowerCase()
                        list.push({ name, url, ext, icon: "", size, type: "" })

                    }
                })
                return list;

            },
            set(value) {
                var str = "";
                value.forEach(file => {
                    str += file.url + ":" + file.name + ":" + file.size + ":" + file.type + ";"
                })
                str = str.substr(0, str.length - 1)
                this.$emit("update:modelValue", str)
            }
        },
        canUpload() {
            return this.disabled == false && this.fileList.length < this.max
        }

    },
    maxSize2() {
        return Math.max(this.max, this.maxSize);
    }

}
</script>

<style scoped>
.image-item {
    position: relative;
    display: inline-block;
    width: 100px;
    height: 100px;
    margin-right: 5px;
    margin-bottom: 5px;
    vertical-align: top;
}

img.image-item {
    width: 100px;
    height: 100px;
}


.remove-btn1 {
    display: block;
    position: absolute;
    top: 0;
    right: 0;
    width: 24px;
    height: 24px;
    font-size: 24px;
    font-weight: bold;
    color: red;
    cursor: pointer;
}

.pic-icon {
    display: inline-block;
    font-size: 98px;
    border: 1px dashed #cecece;
    color: #cecece;
}

.van-image__error-icon {
    color: #dcdee0;
    font-size: 96px !important;
    border: 1px dashed #cecece !important;
}


.van-cell {
    position: relative;
    display: flex;
    box-sizing: border-box;
    width: 100%;
    padding: 2px 6px;
    overflow: hidden;
    color: #283486;
}

.van-cell__title,
.van-cell__value {
    flex: 1;
}

.van-cell__label {
    margin: -4px 4px;
    font-size: 13px;
    line-height: 14px;
    color: #a59d9d;
}
</style>